package com.jike.shop.cloud.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.jike.shop.cloud.entity.User;
import com.jike.shop.cloud.user.dto.Authentication;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class AuthenticationManager implements Serializable {
    /**
     * Token 过期时间
     */
    // TODO 可配置
    static final int ACCESS_TOKEN_EXPIRES_IN = 2 * 60 * 60 * 1000;
    static final int REFRESH_TOKEN_EXPIRES_IN = 7 * 24 * 60 * 60 * 1000;
    /**
     * Token 签名密钥
     */
    static final String SIGN_SECRET = "jike-shop";
    private final String id;
    private final User user;
    private Token accessToken;
    private Token refreshToken;

    public AuthenticationManager(User user) {
        this.id = UUID.randomUUID().toString();
        this.user = user;
        this.accessToken = accessToken();
        this.refreshToken = refreshToken();
    }

    public Authentication create() {
        return build();
    }

    public Authentication refresh() {
        if (accessToken.refresh()) {
            accessToken = accessToken();
        }
        if (refreshToken.refresh()) {
            refreshToken = refreshToken();
        }
        return build();
    }

    private Authentication build() {
        return new Authentication(accessToken.jwt, refreshToken.jwt, ACCESS_TOKEN_EXPIRES_IN);
    }

    private Token accessToken() {
        Date expiresAt = new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRES_IN);

        Map<String, Object> payload = new HashMap<>(1);
        payload.put("name", user.getName());

        String token = JWT.create()
                          .withPayload(payload)
                          .withExpiresAt(expiresAt)
                          .sign(Algorithm.HMAC256(SIGN_SECRET));
        return new Token(token, expiresAt.getTime(), ACCESS_TOKEN_EXPIRES_IN);
    }

    private Token refreshToken() {
        Date expiresAt = new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRES_IN);
        String token = JWT.create()
                          .withJWTId(id)
                          .withExpiresAt(expiresAt)
                          .sign(Algorithm.HMAC256(SIGN_SECRET));
        return new Token(token, expiresAt.getTime(), REFRESH_TOKEN_EXPIRES_IN);
    }

    class Token implements Serializable {
        String jwt;
        long expireAt;
        int expireIn;

        public Token(String jwt, long expireAt, int expireIn) {
            this.jwt = jwt;
            this.expireAt = expireAt;
            this.expireIn = expireIn;
        }

        boolean expired() {
            return System.currentTimeMillis() > expireAt;
        }

        boolean refresh() {
            return System.currentTimeMillis() >= expireAt - expireIn * 0.75;
        }
    }

    public String getId() {
        return id;
    }
}
